﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;

namespace Xceed.Silverlight.Data.Stats
{
  // This class is used by Covariance and CorrelationCoefficient to reduce the number of 
  // passes (Accumulate) and Prerequisites. With this class, the two main function only
  // require 2 passes of the data. Also, more precise prerequisites would be a 
  // bit awkward because they need to only consider pairs of non null values.
  internal class PreCovarianceFunction : CumulativeStatFunction
  {
    internal PreCovarianceFunction()
      : base()
    {
    }

    internal PreCovarianceFunction( string resultPropertyName, string sourcePropertyName )
      : base( resultPropertyName, sourcePropertyName )
    {
    }

    protected internal override void Validate()
    {
      base.Validate();
      this.ValidateSourcePropertyName( 2 );
    }

    protected override void Initialize( Type[] sourcePropertyTypes )
    {
      base.Initialize( sourcePropertyTypes );

      m_accumulationTypeX = StatFunction.GetDefaultNumericalAccumulationType( sourcePropertyTypes[ 0 ] );
      m_accumulationTypeY = StatFunction.GetDefaultNumericalAccumulationType( sourcePropertyTypes[ 1 ] );
    }

    protected internal override void Reset()
    {
      m_sumXLong = 0;
      m_sumXDouble = 0d;
      m_sumXDecimal = 0m;
      m_sumYLong = 0;
      m_sumYDouble = 0d;
      m_sumYDecimal = 0m;
      m_count = 0;
    }

    protected internal override void Accumulate( object[] values )
    {
      if( StatFunction.CanProcessValues( values, 2 ) )
      {
        checked
        {
          switch( m_accumulationTypeX )
          {
            case TypeCode.Int64:
              m_sumXLong += Convert.ToInt64( values[ 0 ] );
              break;

            case TypeCode.Double:
              m_sumXDouble += Convert.ToDouble( values[ 0 ] );
              break;

            case TypeCode.Decimal:
              m_sumXDecimal += Convert.ToDecimal( values[ 0 ] );
              break;
          }

          switch( m_accumulationTypeY )
          {
            case TypeCode.Int64:
              m_sumYLong += Convert.ToInt64( values[ 1 ] );
              break;

            case TypeCode.Double:
              m_sumYDouble += Convert.ToDouble( values[ 1 ] );
              break;

            case TypeCode.Decimal:
              m_sumYDecimal += Convert.ToDecimal( values[ 1 ] );
              break;
          }

          m_count++;
        }
      }
    }

    protected internal override void AccumulateChildResult( StatResult childResult )
    {
      PreCovarianceResult preResult = ( PreCovarianceResult )childResult;

      checked
      {
        switch( m_accumulationTypeX )
        {
          case TypeCode.Int64:
            m_sumXLong += Convert.ToInt64( preResult.SumX );
            break;

          case TypeCode.Double:
            m_sumXDouble += Convert.ToDouble( preResult.SumX );
            break;

          case TypeCode.Decimal:
            m_sumXDecimal += Convert.ToDecimal( preResult.SumX );
            break;
        }

        switch( m_accumulationTypeY )
        {
          case TypeCode.Int64:
            m_sumYLong += Convert.ToInt64( preResult.SumY );
            break;

          case TypeCode.Double:
            m_sumYDouble += Convert.ToDouble( preResult.SumY );
            break;

          case TypeCode.Decimal:
            m_sumYDecimal += Convert.ToDecimal( preResult.SumY );
            break;
        }

        m_count += preResult.Count;
      }
    }

    protected internal override StatResult GetResult()
    {
      object sumX = null;
      object sumY = null;
      object avgX = null;
      object avgY = null;

      if( m_count == 0 )
        throw new DivideByZeroException();

      switch( m_accumulationTypeX )
      {
        case TypeCode.Int64:
          sumX = m_sumXLong;
          avgX = ( double )m_sumXLong / ( double )m_count;
          break;

        case TypeCode.Double:
          sumX = m_sumXDouble;
          avgX = m_sumXDouble / m_count;
          break;

        case TypeCode.Decimal:
          sumX = m_sumXDecimal;
          avgX = m_sumXDecimal / m_count;
          break;
      }

      switch( m_accumulationTypeY )
      {
        case TypeCode.Int64:
          sumY = m_sumYLong;
          avgY = ( double )m_sumYLong / ( double )m_count;
          break;

        case TypeCode.Double:
          sumY = m_sumYDouble;
          avgY = m_sumYDouble / m_count;
          break;

        case TypeCode.Decimal:
          sumY = m_sumYDecimal;
          avgY = m_sumYDecimal / m_count;
          break;
      }

      return new PreCovarianceResult( avgX, avgY, m_count, sumX, sumY );
    }

    private TypeCode m_accumulationTypeX = TypeCode.Empty;
    private TypeCode m_accumulationTypeY = TypeCode.Empty;
    private long m_sumXLong;
    private double m_sumXDouble;
    private decimal m_sumXDecimal;
    private long m_sumYLong;
    private double m_sumYDouble;
    private decimal m_sumYDecimal;
    private long m_count;
  }
}
